package com.duanjw.springframework.util;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @author duanjw
 */
public class BeanFactory {
    private Map<String, Object> map = new HashMap(16);

    public BeanFactory(String path) {
        try {
            parseXml(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析xml
     *
     * @param path
     * @throws Exception
     */
    public void parseXml(String path) throws Exception {
        SAXReader reader = new SAXReader();
        Document document = reader.read(path);
        Element rootElement = document.getRootElement();
        // 是否开启自动注入
        Attribute attribute = rootElement.attribute("default-autowire");
        boolean flag = false;
        if (attribute != null) {
            flag = true;
        }
        // 读取bean标签
        for (Iterator<Element> beanLabel = rootElement.elementIterator(); beanLabel.hasNext(); ) {
            Element element = beanLabel.next();
            Attribute attributeId = element.attribute("id");
            String beanName = attributeId.getValue();

            Attribute attributeClass = element.attribute("class");
            String className = attributeClass.getValue();

            Class<?> clazz = Class.forName(className);
            Object o = null;


            // 读取子标签
            for (Iterator<Element> propertyLabel = element.elementIterator(); propertyLabel.hasNext(); ) {
                Element next = propertyLabel.next();
                // 注入：属性注入
                if (null != next.attribute("property")) {
                    String name = next.attribute("name").getValue();
                    String ref = next.attribute("ref").getValue();

                    // 给属性赋值
                    Field declaredField = clazz.getDeclaredField(name);
                    declaredField.setAccessible(true);
                    o = clazz.getDeclaredConstructor().newInstance();
                    declaredField.set(o, map.get(ref));
                }
                // 注入：构造方法注入
                else {
                    String ref = next.attribute("ref").getValue();
                    Object o1 = map.get(ref);
                    Constructor<?> constructor = clazz.getConstructor(o1.getClass().getInterfaces()[0]);
                    constructor.newInstance(o1);
                }
            }

            if (flag) {
                // 自动注入：根据type
                if (attribute.getValue().equals("byType")) {
                    // 判断属性有依赖，也就是需要注入
                    Field[] declaredFields = clazz.getDeclaredFields();
                    for (Field declaredField : declaredFields) {
                        Class<?> type = declaredField.getType();
                        // 如果有多个可以注入的对象，报错
                        int count = 0;
                        Object thisObject = null;
                        // 查看所有可以注入的对象
                        for(String key : map.keySet()){
                            Object o1 = thisObject =  map.get(key);
                            if (type.getName().equals(o1.getClass().getInterfaces()[0].getName())) {
                                count ++ ;
                            }
                        }
                        if(count > 1) {
                            throw new RuntimeException("需要一个实现，但是找到两个实现");
                        } else if(count > 0) {
                            declaredField.setAccessible(true);
                            o = clazz.getDeclaredConstructor().newInstance();
                            declaredField.set(o, thisObject);
                        }
                    }
                }
            }
            // 没有任何子标签
            if (o == null) {
                o = clazz.getDeclaredConstructor().newInstance();
            }

            map.put(beanName, o);

        }
    }

    /**
     * 获取bean
     *
     * @param name
     * @return
     */
    public Object getBean(String name) {
        return map.get(name);
    }
}
